import { Node, Prefab, Vec3, instantiate } from 'cc';

type PoolNode = Node;

/**
 * 预制体对象池选项
 * @param prefab 预制体
 * @param initalSize 池子初始大小
 * @param maxSize 池子最大大小
 * @param minSize 池子最小大小
 * @param expandRate 扩容倍率
 * @param shrinkDelay 空闲多久后缩容（秒）
 * @param onGetNode 获取节点回调
 * @param onPutNode 放回节点回调
 */
export interface PrefabPoolOptions {
  prefab: Prefab;
  initalSize?: number;
  maxSize?: number;
  minSize?: number;
  expandRate?: number;
  shrinkDelay?: number;
  onGetNode?: (node: Node) => void;
  onPutNode?: (node: Node) => void;
}

export class PrefabPool {
  private pool: PoolNode[] = [];
  private cont: number = 0;
  private lastShrinkTime: number = 0;

  private prefab: Prefab;
  private initialSize: number;
  private maxSize: number;
  private minSize: number;
  private expandRate: number;
  private shrinkDelay: number;
  private onGetNode?: (node: Node) => void;
  private onPutNode?: (node: Node) => void;

  constructor(options: PrefabPoolOptions) {
    this.prefab = options.prefab;
    this.initialSize = options.initalSize ?? 10;
    this.maxSize = options.maxSize ?? 100;
    this.expandRate = options.expandRate ?? 1.5;
    this.shrinkDelay = options.shrinkDelay ?? 5;

    this.onGetNode = options.onGetNode;
    this.onPutNode = options.onPutNode;

    this.initPoll();
  }

  /**
   * 初始化池子
   */
  private initPoll() {
    for (let i = 0; i < this.initialSize; i++) {
      const node = instantiate(this.prefab);
      node.active = false;
      this.pool.push(node);
      this.cont++;
    }
  }

  // 取出预制体
  get(): Node | null {
    if (!this.pool.length) {
      return this.expandPool();
    }
    const node = this.pool.pop()!;
    node.active = true;
    this.onGetNode?.(node);
    return node;
  }


  // 回收预制体
  put(node: Node) {
    if (!node) return;
    node.active = false;
    node.setPosition(Vec3.ZERO);
    this.onPutNode?.(node);
    this.pool.push(node);
    this.tryShrink();
  }

  // 扩容
  expandPool(): Node | null {
    if (this.cont < this.maxSize) {
      const expandCount = Math.min(
        Math.ceil(this.cont * (this.expandRate - 1)),
        this.maxSize - this.cont
      );
      let lastNode: Node | null = null;
      for (let i = 0; i < expandCount; i++) {
        const node = instantiate(this.prefab);
        node.active = false;
        this.pool.push(node);
        this.cont++;
        lastNode = node;
      }
      return this.get();
    } else {
      console.warn('Prefab Pool 已到最大容量');
      return null;
    }
  }

  // 缩容
  private tryShrink() {
    const now = performance.now() / 1000;
    if (now - this.lastShrinkTime < this.shrinkDelay) return;
    this.lastShrinkTime = now;

    const excess = this.pool.length - this.initialSize;
    if (excess <= 0) return;

    for (let i = 0; i < excess; i++) {
      const node = this.pool.pop()!;
      node.destroy();
      this.cont--;
    }
  }

  // 返回池子中剩余预制体数量
  size(): number {
    return this.pool.length;
  }

  // 返回预制体总数
  totalCount(): number {
    return this.cont;
  }
}
